// UEService.cpp
#include <winsock2.h>
#include <tchar.h>

// UELogger Support
#include "litelog.h"

// UESever Common Definitions
#include "UEServer.h"
// Class Definition
#include "UEService.h"
#include "UEServiceWorker.h"

CUEService::CUEService()
{
	m_nListenPort = DEFAULT_SERVICE_PORT;
	m_nListenAddr = inet_addr("127.0.0.1");

	m_ListenSocket = INVALID_SOCKET;
	m_strBaseDir = NULL;
}

CUEService::~CUEService()
{
}

int CUEService::StopService()
{
	int nRet = 0;                           // return code
	int s;                                  // socket file descriptor
	struct sockaddr_in server_addr;         // server address to connect in
	ue_request_header RequestHeader;        // UE request header structure for sending request
	ue_response_header ResponseHeader;      // UE response header structore for receiving response 
	int len;                                // send/recv data length
	dbglog("Enter CUEService::StopService()", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);

	s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (s > 0)
	{
		dbglog("Create socket success, begin connect to server", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);

		// fill in the server address structure
		server_addr.sin_family = AF_INET;
		server_addr.sin_port = htons(DEFAULT_SERVICE_PORT);
		server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");

		// connect to server
		if (connect(s, (struct sockaddr*)&server_addr, sizeof(server_addr)) == 0)
		{
			dbglogex("Connect toport %d of 127.0.0.1 success.", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__, DEFAULT_SERVICE_PORT);

			// fill in the request for stop service
			RequestHeader.addr = inet_addr("127.0.0.1");
			RequestHeader.identifer = GetCurrentProcessId();
			RequestHeader.port = 0;
			RequestHeader.len = 0;
			RequestHeader.version = 1;
			RequestHeader.request_type = UE_REQ_TYPE_STOP;

			len = send(s, (const char*)&RequestHeader, sizeof(ue_request_header), 0);
			if (len == sizeof(ue_request_header))
			{
				dbglog("Send stop service request success.", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);

				len = recv(s, (char*)&ResponseHeader, sizeof(ue_response_header), 0);
				if (len == sizeof(ue_response_header))
				{
					dbglog("Read stop service response success.", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);
					
					// verify if the reponse is valid
					if (RequestHeader.addr == ResponseHeader.addr && RequestHeader.identifer == ResponseHeader.identifer 
						&& RequestHeader.port == ResponseHeader.port && RequestHeader.sequence == ResponseHeader.sequence)
					{
						if (!ResponseHeader.status)
						{
							dbglog("Server response stop service success.", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);
						} // server handle stop service request success
						else
						{
							dbglogex("Server response stop service failed. Response Status: %d", DEBUG_LOG_LEVEL_ERRO, logfd, __FILE__, __LINE__, ResponseHeader.status);
							nRet = 3;
						} // server handle stop service request failed
					}
					else
					{
						dbglog("Invalid response for stop service request. Possibly the server is not UEService.", DEBUG_LOG_LEVEL_ERRO, logfd, __FILE__, __LINE__);
						nRet = 2;
					} // response verified failed
				} // read response for stop service success
				else
				{
					dbglogex("Read response for stop service failed. Error Code: %d", DEBUG_LOG_LEVEL_ERRO, logfd, __FILE__, __LINE__, WSAGetLastError());
					nRet = -3;
				} // read response for stop service failed
			} // send stop service request success
			else
			{
				dbglogex("Send stop service request failed. Error Code: %d", DEBUG_LOG_LEVEL_ERRO, logfd, __FILE__, __LINE__, WSAGetLastError());
				nRet = -2;
			} // send stop service request failed
		} // connect to server success 
		else
		{
			dbglogex("Connect to port: %d of 127.0.0.1 failed, UEService should not be started", DEBUG_LOG_LEVEL_ERRO, logfd, __FILE__, __LINE__, DEFAULT_SERVICE_PORT);

			nRet = 1;
		} // connect to server failed

		closesocket(s);
	}
	else
	{
		dbglogex("Create socket to connect server failed. Error code: %d", DEBUG_LOG_LEVEL_ERRO, logfd, __FILE__, __LINE__, WSAGetLastError());
		nRet = -1;
	}

	dbglog("Exit CUEService::StopService()", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);

	return nRet;
}

int CUEService::Start(_TCHAR *strBaseDir)
{
	int nRet = 0;                           // return code
	int c;                                  // client socket file descriptor
	struct sockaddr_in listen_addr;         // address to assign to the listen socket (bind socket to this address)
	struct sockaddr_in from_addr;           // address of the client
	int len;                                // to store the size of from_addr
	dbglog("Enter CUEService::Start()", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);

	m_strBaseDir = strBaseDir;

	if (CUEServiceWorker::GetEncoderClsid(UE_IMAGE_FORMAT, &CUEServiceWorker::s_imgClsid))
	{
		dbglog("Get class identifier for encoder for GDI+ used by UEServer success.", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);

		if ((m_ListenSocket = socket(AF_INET, SOCK_STREAM, 0)) > 0)
		{
			dbglog("Create socket for listen success", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);

			memset(&listen_addr, 0, sizeof(listen_addr));
			listen_addr.sin_family = AF_INET;
			listen_addr.sin_port = htons(m_nListenPort);
			listen_addr.sin_addr.s_addr = m_nListenAddr;

			if (bind(m_ListenSocket, (struct sockaddr*)&listen_addr, sizeof(listen_addr)) == 0 ) 
			{
				dbglogex("Place socket in listen state on port %d of address %x success", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__, m_nListenPort, m_nListenAddr);

				if (listen(m_ListenSocket, SOMAXCONN) == 0)
				{
					dbglog("Begin the main loop to accept client connections", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);
					while (1)
					{
						len = sizeof(from_addr);
						c = accept(m_ListenSocket, (struct sockaddr*)&from_addr, &len);
						if (c > 0)
						{
							CUEServiceWorker* pWorker = new CUEServiceWorker(this, c, from_addr.sin_addr.s_addr, ntohs(from_addr.sin_port));
							if (pWorker != NULL)
							{
								DWORD thread_id;
								HANDLE hWorkingThread = CreateThread(NULL, 0, CUEServiceWorker::WorkingThread, pWorker, 0, &thread_id);
								if (hWorkingThread)
								{
									dbglogex("Create workding thread success for connection from client addr: %x, port: %d", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__, from_addr.sin_addr.s_addr, ntohs(from_addr.sin_port));
									CloseHandle(hWorkingThread);
								} // create working thread success
								else
								{
									dbglogex("Create workding thread failed. Error Num: %d", DEBUG_LOG_LEVEL_ERRO, logfd, __FILE__, __LINE__, GetLastError());
									closesocket(c);
									delete pWorker;
								} // create working thread failed
							} // malloc memory for new CUEServiceWorker success
							else
							{
								closesocket(c);
								dbglogex("Create new CUEServiceWorker failed for connection from client addr: %x, port: %d", DEBUG_LOG_LEVEL_ERRO, logfd, __FILE__, __LINE__, from_addr.sin_addr.s_addr, ntohs(from_addr.sin_port));
							} // malloc memory for new CUEServiceWorker failed
						} // accept client connetion success
						else
						{
							dbglogex("Failed to accept client connection. Error Num: %d", DEBUG_LOG_LEVEL_WARN, logfd, __FILE__, __LINE__, WSAGetLastError());
							/* Accept connection failed, but we can still continue to try to access other connections until the listen socket is invalid */
							if (m_ListenSocket <= 0)
								break;
						} // accept client connetion failed
					} // main loop
				} // socket listen success
				else
				{
					dbglogex("Socket listen to port %d failed, possible the address is in use. Error Num: %d", DEBUG_LOG_LEVEL_ERRO, logfd, __FILE__, __LINE__, m_nListenPort, WSAGetLastError());
					nRet = 4;
				} // socket listen failed
			} // bind socket to specified address success
			else
			{
				dbglogex("Bind to port %d failed, possible the address is in use. Error Num: %d", DEBUG_LOG_LEVEL_ERRO, logfd, __FILE__, __LINE__, m_nListenPort, WSAGetLastError());
				nRet = 3;
			} // bind socket to specified address failed
		} // Create socket for listen success
		else
		{
			dbglogex("Create socket for listen failed. Error Num: %d", DEBUG_LOG_LEVEL_ERRO, logfd, __FILE__, __LINE__, WSAGetLastError());
			nRet = 2;
		} // Create socket for listen failed
	} // Get class identifier for encoder for GDI+ used by UEServer success
	else
	{
		dbglog("Get class identifier for encoder for GDI+ used by UEServer failed.", DEBUG_LOG_LEVEL_ERRO, logfd, __FILE__, __LINE__);
		nRet = 1;
	} // Get class identifier for encoder for GDI+ used by UEServer failed

	dbglog("Exit CUEService::Start()", DEBUG_LOG_LEVEL_INFO, logfd, __FILE__, __LINE__);

	return nRet;
}

void CUEService::Stop()
{
	if (m_ListenSocket > 0)
	{
		closesocket(m_ListenSocket);
		m_ListenSocket = INVALID_SOCKET;
	}
}